package com.hanains.spring;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AbstractRefreshableWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.ModelAndView;

/**
 * 
 * @author Jiho
 * @since 2012. 11. 9.
 * 
 */
public class HanaDispatcherServlet extends DispatcherServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private static Log LOG = LogFactory.getLog(HanaDispatcherServlet.class);

	private Class<?>[] classes;
	private String[] locations;

	private ModelAndView modelAndView;

	public HanaDispatcherServlet(String[] locations) {
		if (LOG.isDebugEnabled()) {
			LOG.debug("locations=" + locations);
		}

		this.locations = locations;
	}

	public HanaDispatcherServlet(Class<?>... classes) {
		if (LOG.isDebugEnabled()) {
			LOG.debug("classes=" + classes);
		}

		this.classes = classes;
	}

	/**
	 * @param locations
	 *            the locations to set
	 */
	public void setLocations(String[] locations) {
		this.locations = locations;
	}

	public void setRelativeLocations(Class clazz, String... relativeLocations) {
		String[] locations = new String[relativeLocations.length];
		String currentPath = ClassUtils.classPackageAsResourcePath(clazz) + "/";

		for (int i = 0; i < relativeLocations.length; i++) {
			locations[i] = currentPath + relativeLocations[i];
		}
		setLocations(locations);
	}

	/**
	 * @param classes
	 *            the classes to set
	 */
	public void setClasses(Class<?>[] classes) {
		this.classes = classes;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.servlet.http.HttpServlet#service(javax.servlet.ServletRequest,
	 * javax.servlet.ServletResponse)
	 */
	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		modelAndView = null;
		super.service(req, res);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.web.servlet.FrameworkServlet#createWebApplicationContext
	 * (org.springframework.context.ApplicationContext)
	 */
	@Override
	protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
		AbstractRefreshableWebApplicationContext wac = new AbstractRefreshableWebApplicationContext() {

			@Override
			protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
				if (locations != null) {
					XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(beanFactory);
					xmlReader.loadBeanDefinitions(locations);
				}

				if (classes != null) {
					AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(beanFactory);
					reader.register(classes);
				}
			}
		};

		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.refresh();

		return wac;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.web.servlet.DispatcherServlet#render(org.springframework
	 * .web.servlet.ModelAndView, javax.servlet.http.HttpServletRequest,
	 * javax.servlet.http.HttpServletResponse)
	 */
	@Override
	protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		this.modelAndView = mv;
		super.render(mv, request, response);
	}

	/**
	 * @return the modelAndView
	 */
	public ModelAndView getModelAndView() {
		return modelAndView;
	}

}
